from ctypes import c_char_p, c_int, c_float, addressof, create_string_buffer, WinDLL

ERR_SIZE = 256
RESULT_SIZE = 256 * 256
dll = None


def load_dll(dll_path):
    global dll
    try:
        dll = WinDLL(dll_path)
        dll.OpenTdx()
        print('load trade.dll success')
    except OSError as e:
        raise OSError(f'load {dll_path} error: {e}')


def dll_call(func_name, *args):
    func = getattr(dll, func_name)
    try:
        return func(*args)
    except OSError:
        pass


def logon(ip, port, ver, yyb_id, account, password):
    c_ip = c_char_p(ip.encode('ascii'))
    c_ver = c_char_p(ver.encode('ascii'))
    c_account = c_char_p(account.encode('ascii'))
    c_password = c_char_p(password.encode('ascii'))
    c_err = c_char_p(('/0' * ERR_SIZE).encode('ascii'))

    client_id = dll_call('Logon', c_ip, port, c_ver, yyb_id, c_account, c_account, c_password, c_password, c_err)
    return {
        'client_id': client_id,
        'error': c_err.value.decode('gbk'),
        'success': client_id != -1
    }


def query_data(client_id, category):
    c_err = create_string_buffer(ERR_SIZE)
    c_res = create_string_buffer(RESULT_SIZE)
    dll_call('QueryData', client_id, category, c_res, c_err)
    return get_result(c_res, c_err)


def send_order(client_id, category, price_type, gddm, zqdm, price, quantity):
    c_err = create_string_buffer(ERR_SIZE)
    c_res = create_string_buffer(RESULT_SIZE)
    c_gddm = c_char_p(gddm.encode('ascii'))
    c_zqdm = c_char_p(zqdm.encode('ascii'))
    c_price = c_float(price)
    dll_call('SendOrder', client_id, category, price_type, c_gddm, c_zqdm, c_price, quantity, c_res, c_err)
    return get_result(c_res, c_err)


def send_orders(client_id, category_list, price_type_list, gddm_list, zqdm_list, price_list, quantity_list, cnt):
    category_list = (c_int * cnt)(*category_list)
    price_type_list = (c_int * cnt)(*price_type_list)
    gddm_list = (c_char_p * cnt)(*[e.encode('ascii') for e in gddm_list])
    zqdm_list = (c_char_p * cnt)(*[e.encode('ascii') for e in zqdm_list])
    price_list = (c_float * cnt)(*price_list)
    quantity_list = (c_int * cnt)(*quantity_list)
    _c_err_list = [create_string_buffer(ERR_SIZE) for i in range(cnt)]
    _c_res_list = [create_string_buffer(RESULT_SIZE) for i in range(cnt)]
    c_err_list = (c_char_p * cnt)(*map(addressof, _c_err_list))
    c_res_list = (c_char_p * cnt)(*map(addressof, _c_res_list))
    dll_call('SendOrders', client_id, category_list, price_type_list, gddm_list, zqdm_list,
             price_list, quantity_list, cnt, c_res_list, c_err_list)
    return get_results(c_res_list, c_err_list)


def cancel_order(client_id, exchange_id, hth):
    c_err = create_string_buffer(ERR_SIZE)
    c_res = create_string_buffer(RESULT_SIZE)
    c_exchange_id = c_char_p(exchange_id.encode('ascii'))
    c_hth = c_char_p(hth.encode('ascii'))
    dll_call('CancelOrder', client_id, c_exchange_id, c_hth, c_res, c_err)
    return get_result(c_res, c_err)


def cancel_orders(client_id, exchange_id_list, hth_list, cnt):
    exchange_id_list = (c_char_p * cnt)(*[e.encode('ascii') for e in exchange_id_list])
    hth_list = (c_char_p * cnt)(*[e.encode('ascii') for e in hth_list])
    _c_err_list = [create_string_buffer(ERR_SIZE) for i in range(cnt)]
    _c_res_list = [create_string_buffer(RESULT_SIZE) for i in range(cnt)]
    c_err_list = (c_char_p * cnt)(*map(addressof, _c_err_list))
    c_res_list = (c_char_p * cnt)(*map(addressof, _c_res_list))
    dll_call('CancelOrders', client_id, exchange_id_list, hth_list, cnt, c_res_list, c_err_list)
    return get_results(c_res_list, c_err_list)


def get_quote(client_id, zqdm):
    c_err = create_string_buffer(ERR_SIZE)
    c_res = create_string_buffer(RESULT_SIZE)
    c_zqdm = c_char_p(zqdm.encode('ascii'))
    dll_call('GetQuote', client_id, c_zqdm, c_res, c_err)
    return get_result(c_res, c_err)


def get_result(c_res, c_err):
    return {
        'success': bool(c_res.value),
        'data': parse(c_res.value.decode('gbk')),
        'error': c_err.value.decode('gbk')
    }


def get_results(c_res_list, c_err_list):
    return [{'success': bool(r),
             'data': parse(r.decode('gbk')),
             'error': e.decode('gbk')} for r, e in zip(c_res_list, c_err_list)]


def parse(data):
    ret = []
    data_lines = data.split('\n')
    title = data_lines[0].split('\t')
    for e in data_lines[1:]:
        if not e:
            continue
        record = {}
        line = e.split('\t')
        for i in range(len(line)):
            record[title[i]] = line[i]
        ret.append(record)
    return ret
